import paramiko
import os
import shutil
from contextlib import contextmanager
from typing import Optional

class SSHConnectionError(Exception):
    """自定义SSH连接异常"""
    pass

@contextmanager
def ssh_connection(host: str, username: str, password: str, timeout: int = 10) -> paramiko.SSHClient:
    """SSH连接上下文管理器"""
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    try:
        client.connect(hostname=host, username=username, password=password, timeout=timeout)
        yield client
    except paramiko.AuthenticationException:
        raise SSHConnectionError("认证失败，请检查用户名和密码") from None
    except paramiko.SSHException as e:
        raise SSHConnectionError(f"SSH连接错误: {str(e)}") from e
    finally:
        client.close()

def execute_command_on_remote(host: str, username: str, password: str, command: str = 'ls -l', timeout: int = 10) -> None:
    """执行远程命令"""
    try:
        with ssh_connection(host, username, password, timeout) as client:
            stdin, stdout, stderr = client.exec_command(command, timeout=timeout)
            output = stdout.read().decode('utf-8').strip()
            error = stderr.read().decode('utf-8').strip()
            
            print("命令输出:")
            print("="*40)
            print(output or "[无输出]")
            
            if error:
                print("\n错误信息:")
                print("="*40)
                print(error)
                
    except SSHConnectionError as e:
        print(f"连接失败: {str(e)}")
    except Exception as e:
        print(f"执行异常: {str(e)}")

@contextmanager
def sftp_connection(host: str, username: str, password: str) -> paramiko.SFTPClient:
    """SFTP连接上下文管理器"""
    transport = paramiko.Transport((host, 22))
    try:
        transport.connect(username=username, password=password)
        sftp = paramiko.SFTPClient.from_transport(transport)
        yield sftp
    except paramiko.AuthenticationException:
        raise SSHConnectionError("SFTP认证失败") from None
    except paramiko.SSHException as e:
        raise SSHConnectionError(f"SFTP连接错误: {e}") from e
    finally:
        if transport:
            transport.close()

def upload_file(host: str, username: str, password: str, local_path: str, remote_dir: str = '/root/python') -> None:
    """文件上传"""
    remote_path = os.path.join(remote_dir, os.path.basename(local_path)).replace('\\', '/')
    
    try:
        with sftp_connection(host, username, password) as sftp:
            # 自动创建远程目录
            try:
                sftp.stat(remote_dir)
            except FileNotFoundError:
                sftp.mkdir(remote_dir, mode=0o755)
            
            # 上传文件
            sftp.put(local_path, remote_path)
            print(f"\n✅ 文件上传成功:")
            print(f"本地路径: {local_path}")
            print(f"远程路径: {remote_path}")
            
    except SSHConnectionError as e:
        print(f"连接失败: {str(e)}")
    except OSError as e:
        print(f"文件操作失败: {str(e)}")
    except Exception as e:
        print(f"上传异常: {str(e)}")

def download_file(host: str, username: str, password: str, remote_path: str, local_dir: str = '.') -> None:
    """文件下载"""
    filename = os.path.basename(remote_path)
    local_path = os.path.join(local_dir, filename).replace('\\', '/')
    
    try:
        with sftp_connection(host, username, password) as sftp:
            # 确保本地目录存在
            os.makedirs(local_dir, exist_ok=True)
            
            # 下载文件
            sftp.get(remote_path, local_path)
            print(f"\n✅ 文件下载成功:")
            print(f"远程路径: {remote_path}")
            print(f"本地路径: {local_path}")
            
    except SSHConnectionError as e:
        print(f"连接失败: {str(e)}")
    except OSError as e:
        print(f"文件操作失败: {str(e)}")
    except Exception as e:
        print(f"下载异常: {str(e)}")

def copy_file(src: str, dst: str) -> None:
    """增强版文件复制（保留元数据）"""
    try:
        if not os.path.exists(src):
            raise FileNotFoundError(f"源文件不存在: {src}")
            
        dst_dir = os.path.dirname(dst)
        if dst_dir and not os.path.exists(dst_dir):
            os.makedirs(dst_dir, exist_ok=True)
            
        shutil.copy2(src, dst)
        print(f"\n✅ 文件复制成功:")
        print(f"源路径: {src}")
        print(f"目标路径: {dst}")
        
    except Exception as e:
        print(f"复制失败: {str(e)}")

def delete_directory(path: str) -> None:
    """安全删除目录（带二次确认）"""
    if not os.path.exists(path):
        print(f"目录不存在: {path}")
        return
        
    if not os.path.isdir(path):
        print(f"非目录路径: {path}")
        return
        
    confirm = input(f"确定要删除目录 {path} 及其所有内容吗？(y/N): ").strip().lower()
    if confirm != 'y':
        print("操作已取消")
        return
        
    try:
        shutil.rmtree(path)
        print(f"\n🗑️ 目录已删除: {path}")
    except Exception as e:
        print(f"删除失败: {str(e)}")

if __name__ == "__main__":
    CONFIG = {
        "host": '172.40.0.190',
        "username": 'root',
        "password": 'Yu641641.',
        "test_file": 'name_abbr.txt',
        "student_file": 'student_id.txt'
    }

    for filename in [CONFIG['test_file'], CONFIG['student_file']]:
        if not os.path.exists(filename):
            with open(filename, 'w', encoding='utf-8') as f:
                f.write(f"测试内容: {filename}\n")
            print(f"已创建测试文件: {filename}")

    execute_command_on_remote(**CONFIG)
    upload_file(CONFIG['host'], CONFIG['username'], CONFIG['password'], CONFIG['test_file'])
    download_file(CONFIG['host'], CONFIG['username'], CONFIG['password'], '/root/python/name_abbr.txt', './downloads')
    copy_file(CONFIG['student_file'], 'backup/student_id_backup.txt')
    delete_directory('./downloads')
    delete_directory('backup')